%%% ====================================================================
%%% Translates the XSD into the model used by erlsom_parse.
%%% 
%%% Copyright (C) 2006 Willem de Jong
%%%
%%% This program is free software; you can redistribute it and/or modify
%%% it under the terms of the GNU General Public License as published by 
%%% the Free Software Foundation; either version 2 of the License, or 
%%% (at your option) any later version.
%%%
%%% This program is distributed in the hope that it will be useful, but
%%% WITHOUT ANY WARRANTY; without even the implied warranty of
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
%%% General Public License for more details.
%%%
%%% You should have received a copy of the GNU General Public License 
%%% along with this program; if not, write to the Free Software 
%%% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
%%% USA
%%%
%%% Author contact: w.a.de.jong@gmail.com
%%% ====================================================================

%%% Version: 0.0.3

%% transform the xsd (in record form, as delivered by erlsom!) into
%% the model required by erlsom.
%%
%% - import (and parse) imported XSDs (adding them to the list of 
%%   types)
%% - perform some additional checks on the validity of the XSD
%% - add sequence numbers and element counts 
%% - add names to the types that include the prefix and the 'path'
%% - groups and 'anonymous sequences' have to be 'linked in' by 
%%   providing alternatives ('real' = false).
%% - 'anonymous' sequences have to get a (unique) name
%% - a top level element ('_document') has to be added to includes
%%   alternatives for all globally declared elements
%% - special treatment is necessary for choices where (some or all) 
%%   alternatives have the same type: otherwise it will not be possible
%%   to recognise from the result which alternative was selected. For 
%%   the moment this is only implented for 'text' alternatives.

%% Ideally it should also take care of:
%% - attribute groups
%% - 'extends'


%% The first pass puts all types and elements into lists. Locally 
%% defined types ('russian doll design') are given names that reflect 
%% the fact that they are local, in order to avoid name conflicts (and 
%% they are explicitly marked as local, to avoid including then in the 
%% '_model' element).

%% result of pass1:
%% 
%% types are distinguished from elements, because only elements can be
%% used as 'root' of the XML document.

-module(erlsom_compile).
-record(p1acc, {tps = [],       %% types
                attGrps = [],   %% attributeGroups
                atts = [],      %% global Attributes
                seqCnt = 0,     %% sequence counter, make unique names
                tns,            %% target namespace
                efd,            %% element form default
                nsp,            %% namespacePrefix
                nss,            %% namespaces ([#namespace{}])
                path = []}).    %% path


-export([compile/3]).
-export([compile_parsed_xsd/3]).

-include("erlsom.hrl").
-include("erlsom_parse.hrl").
-include("erlsom_compile.hrl").

-import('erlsom_lib', [minMax/1]).

%% debug(Text) -> io:format("compile - ~p\n", [Text]).

compile(Xsd, Prefix, Namespaces) ->
  %% first we translate to an intermediate format, in a final pass we will add
  %% things like sequence numbers and element counts.
  %% debug("compile"),
  %% -record(ns, {uri, prefix}).
  Ns = [#ns{prefix="xsd", uri="http://www.w3.org/2001/XMLSchema"} | Namespaces],
  ParsingResult = erlsom_parseXsd:parseXsd(Xsd, Ns),
  case ParsingResult of
    {error, _Message} -> ParsingResult;
    {ok, ParsedXsd} -> compile_parsed_xsd(ParsedXsd, Prefix, Ns)
  end.
      
compile_parsed_xsd(ParsedXsd, Prefix, Namespaces) ->
  %% InfoRecord will contain some information required along the way
  TargetNs = ParsedXsd#schemaType.targetNamespace,
  Acc = #p1acc{tns = TargetNs,
	       efd = ParsedXsd#schemaType.elementFormDefault, 
	       nsp = case TargetNs of
                       undefined -> "";
                       _ -> Prefix ++ ":"
                     end,
	       nss = case TargetNs of
                       undefined -> Namespaces;
                       _ -> [#ns{prefix = Prefix, uri = TargetNs} | Namespaces]
                     end},
  case catch transform(ParsedXsd, Acc) of
    {error, Message} -> {error, Message};
    {'EXIT', Message} -> throw({'EXIT', Message});
    IntermediateResult -> 
      case catch erlsom_pass2:secondPass(IntermediateResult#p1acc.tps,
					 #schemaInfo{targetNamespace = IntermediateResult#p1acc.tns,
						     namespaces = IntermediateResult#p1acc.nss,
						     atts = IntermediateResult#p1acc.atts,
						     attGrps = IntermediateResult#p1acc.attGrps}) of
	 {error, Message} -> {error, Message};
	 FinalResult -> {ok, FinalResult}
      end
  end.

%% -record(schemaType, {targetNamespace, elementFormDefault, elements}).
transform(Xsd = #schemaType{elements=Elements, imports=Impts}, 
          Acc = #p1acc{}) ->
  testSchema(Xsd),
  %% debug("transform"),
  %% transorm element and types etc.
  %% in TransformedTypes the top-level types have to be recognisable somehow.
  Acc2 = transformTypes(Elements, Acc),
  %% debug("transformed types"),
  %% debug(TransformedTypes),
  %% debug("process Imports"),
  processImports(Impts, Acc2).

testSchema(#schemaType{attributeFormDefault = AFD}) ->
  case AFD of 
    "unqualified" -> ok;
    undefined -> ok;
    _ -> throw({error, "attributeFormDefault not supported"})
  end.

%%
processImports(undefined, Acc) ->
  Acc;
processImports([], Acc) ->
  Acc;
processImports([#annotationType{} | Tail], Acc) ->
  processImports(Tail, Acc);
processImports([Impt = #importType{} | Tail], Acc = #p1acc{}) ->
  %% read file
  case erlsom_lib:findFile(Impt#importType.namespace, Impt#importType.schemaLocation) of
    {ok, ImportFile} ->
      Xsd = readXsdFile(ImportFile),
      %% debug(Xsd),
      {ok, ParsedGrammar} = erlsom_parseXsd:parseXsd(Xsd, Acc#p1acc.nss),
      Acc2 = combineInfo(ParsedGrammar, Acc),
      %% debug(ParsedGrammar),
      %% transform (recursively)
      Acc3 = transform(ParsedGrammar, Acc2),
      Acc4 = Acc#p1acc{tps = Acc3#p1acc.tps,
                       seqCnt = Acc3#p1acc.seqCnt,
                       attGrps = Acc3#p1acc.attGrps,
                       atts = Acc3#p1acc.atts},
      %% processImports(Tail, transform(ParsedGrammar, Acc3)).
      processImports(Tail, Acc4);
    _ ->
      processImports(Tail, Acc)
  end;
processImports([#includeType{schemaLocation = undefined} | Tail], Acc) ->
  processImports(Tail, Acc);
processImports([Impt = #includeType{} | Tail], Acc = #p1acc{}) ->
  %% read file
  Xsd = readXsdFile(Impt#includeType.schemaLocation),
  %% debug("Parse imported file"),
  %% debug(Xsd),
  {ok, ParsedGrammar} = erlsom_parseXsd:parseXsd(Xsd, Acc#p1acc.nss),
  Acc2 = combineInfo(ParsedGrammar, Acc),
  %% debug(ParsedGrammar),
  %% transform (recursively)
  Acc3 = transform(ParsedGrammar, Acc2),
  Acc4 = Acc#p1acc{tps = Acc3#p1acc.tps,
                   seqCnt = Acc3#p1acc.seqCnt,
                   attGrps = Acc3#p1acc.attGrps,
                   atts = Acc3#p1acc.atts},
  %% processImports(Tail, transform(ParsedGrammar, Acc3)).
  processImports(Tail, Acc4).

readXsdFile(SchemaLocation) ->
  XsdFile = SchemaLocation,
  case file:read_file(XsdFile) of
    {ok, Bin} ->
      binary_to_list(Bin);
    Error ->
      Error
  end.

%% Deals with targetNamespace and 'elementFormDefault' setting. 
%% information from this schema is combined with info from a potential 'parent' XSD. 
combineInfo(#schemaType{targetNamespace=Tns, elementFormDefault=Efd}, 
            Acc = #p1acc{tns=TnsParent, efd = EfdParent, nss = Namespaces}) ->
   Acc#p1acc{tns = if 
                      Tns == undefined -> TnsParent; 
                      true -> Tns
                   end,
             efd = if 
                      %% what is right???
                      %% Efd == undefined -> EfdParent;
                      true -> Efd
                    end,
             nsp = erlsom_lib:findPrefix(Tns, Namespaces)}.

%% globalElementType
%% -record(globalElementType, {name, type, simpleOrComplex}).
transformTypes([#globalElementType{name=Name, type=undefined, simpleOrComplex=SorC}| Tail],
                Acc = #p1acc{nsp = Prefix}) ->
   case SorC of 
      %% #localSimpleTypeType{} ->
         %% Elements = translateSimpleType(Name, SorC, Info),
      #localComplexTypeType{} ->
         {Type, Acc2} = translateLocalComplexType(SorC, Acc#p1acc{path = Name ++ "/"}),
         ResultSoFar = Acc2#p1acc.tps
   end,
   Type2 = Type#typeInfo{typeName= Prefix ++ Name, global = true, 
                         typeType = globalElement},
   transformTypes(Tail, Acc2#p1acc{path = [], tps = [Type2 | ResultSoFar]});

transformTypes([#globalElementType{name=Name, type=Type, simpleOrComplex=undefined}| Tail],
                Acc = #p1acc{tps = ResultSoFar, nsp = Prefix, nss = Nss}) ->
   ThisType = #typeInfo{typeName= Prefix ++ Name, global = true
              ,         typeRef = erlsom_lib:makeType(Type, Nss)
              ,         typeType = globalElementRefOnly},
              %%,         typeType = globalElement},
   transformTypes(Tail, Acc#p1acc{tps = [ThisType | ResultSoFar]});

transformTypes([#globalComplexTypeType{name=Name, model=Model, attributes=Attributes, anyAttribute=AnyAttr, mixed = Mixed} |
                Tail],
                Acc = #p1acc{nsp = Prefix}) ->
   Path = Name ++ "/",
   {Type, Acc2} = translateComplexTypeModel(Model, Acc#p1acc{path = Path}),
   TheAttributes = Type#typeInfo.attributes ++ translateAttributes(Attributes, Acc2),
   AnyAttrValue = if 
                     AnyAttr /= undefined -> translateAnyAttrValue(AnyAttr);
		     true -> Type#typeInfo.anyAttr
		  end,
   ResultSoFar = Acc2#p1acc.tps,
   Type2 = Type#typeInfo{typeName= Prefix ++ Name, global = true, 
                         attributes=TheAttributes,
			 anyAttr = AnyAttrValue,
                         mixed = Mixed,
                         typeType = type},
   transformTypes(Tail, Acc2#p1acc{path = [], tps = [Type2 | ResultSoFar]});

%%-record(globalSimpleTypeType, {name, annotation, model}).
transformTypes([#globalSimpleTypeType{name=Name, model=undefined}| Tail],
                Acc = #p1acc{tps = ResultSoFar, nsp = Prefix}) ->
   %% Element = #elementInfo{alternatives=[#alternative{tag="#text", type="##string", real=false}], 
                                             %% min=1, max=1}, 
   %% Type = #typeInfo{typeName= Prefix ++ Name, global = false, 
                    %% elements = [Element],
                    %% typeType = type},
   %% transformTypes(Tail, Acc#p1acc{tps = [Type | ResultSoFar]});
   %%% relpaced the above by:
   Type = #typeInfo{typeName = Prefix ++ Name, typeRef = "##string", global = false, 
                            typeType = simpleType},
   transformTypes(Tail, Acc#p1acc{tps = [Type | ResultSoFar]});

%%-record(groupDefType, {name, annotation, model}).
transformTypes([GroupDef = #groupDefType{}| Tail], Acc) ->
   {Type, Acc2} = transformGroupDef(GroupDef, Acc),
   ResultSoFar = Acc2#p1acc.tps,
   transformTypes(Tail, Acc2#p1acc{tps = [Type | ResultSoFar]});

%% -record(attributeGroupDefType, {id, name, annotation, attributes, anyAttribute}).
transformTypes([#attributeGroupDefType{name=Name, attributes=Atts, anyAttribute = AnyAttr}|Tail], 
                Acc = #p1acc{}) ->
  %% parse the attributes
  AttGrp = #attGrp{name= Name, atts = translateAttributes(Atts, Acc),
                   anyAttr = translateAnyAttrValue(AnyAttr)},
  ResultSoFar = Acc#p1acc.attGrps,
  transformTypes(Tail, Acc#p1acc{attGrps = [AttGrp | ResultSoFar]});

%% -record(globalAttributeType, {elInfo, name, type, use, model}).
transformTypes([#globalAttributeType{name=Name, type=Type, use = Use} |Tail], 
                Acc = #p1acc{nsp = Prefix}) ->
  Att = #attribute{name = Prefix ++ Name, type=Type, optional=Use},
  ResultSoFar = Acc#p1acc.atts,
  transformTypes(Tail, Acc#p1acc{atts = [Att | ResultSoFar]});

transformTypes([#annotationType{}|Tail], Acc) ->
  transformTypes(Tail, Acc);

transformTypes([], Acc = #p1acc{tps = Types}) ->
  Acc#p1acc{tps = lists:reverse(Types)}.

%%-record(groupDefType, {name, annotation, model}).
transformGroupDef(#groupDefType{name=Name, model=Model}, 
                  Acc = #p1acc{nsp = Prefix}) ->
   Path = Name ++ "/",
   {Type, Acc2} = translateComplexTypeModel(Model, Acc#p1acc{path = Path}),
   {Type#typeInfo{typeName= Prefix ++ Name, global = false, typeType = type}, Acc2}.


%% -record(localComplexTypeType, {annotation, model, attributes, anyAttribute}).
translateLocalComplexType(#localComplexTypeType{model=Model, attributes=Attributes, anyAttribute=AnyAttr, mixed = Mixed},
                          Acc = #p1acc{}) ->
   {Type, Acc2} = translateComplexTypeModel(Model, Acc),
   TheAttributes = Type#typeInfo.attributes ++ translateAttributes(Attributes, Acc2),
   AnyAttrValue = translateAnyAttrValue(AnyAttr),
   {Type#typeInfo{attributes = TheAttributes, global = false, anyAttr = AnyAttrValue, mixed = Mixed}, Acc2}. 
   %% global = false???

%% -record(anyAttributeType, {elInfo, id, namespace, processContents, annotation}).
translateAnyAttrValue(undefined) ->
  undefined;
translateAnyAttrValue(#anyAttributeType{namespace = Ns, processContents = Pc}) ->
%% -record(anyAttr, {prCont, ns}).
  #anyAttr{prCont = Pc, ns = Ns}.


%% -record(simpleContentType, {annotation, model}).
%% -record(extensionType, {annotation, attributes, anyAttribute}).
%% This is a bit weird - see it as a hook to do something else with the simpleContentType
translateSimpleContentTypeModel(#simpleContentType{model=#extensionType{attributes=Attributes, anyAttribute = AnyAttr}}, Acc) ->
   TheAttributes = translateAttributes(Attributes, Acc),
   AnyAttrValue = translateAnyAttrValue(AnyAttr),
   {TheAttributes, AnyAttrValue};
translateSimpleContentTypeModel(#simpleContentType{}, _Acc) ->
   {[], undefined}.

translateAttributes(undefined, _Acc) ->
  [];

translateAttributes(Attributes, Acc) ->
  translateAttributes(Attributes, Acc, []).

translateAttributes([Attribute | Tail], Acc, ResultSoFar) ->
  translateAttributes(Tail, Acc, [translateAttribute(Attribute, Acc) | ResultSoFar]);
 
translateAttributes([], _Acc, ResultSoFar) ->
  lists:reverse(ResultSoFar).

%% -record(localAttributeType, {name, type, use, model}).
%% TODO: something with attributeFormDefault (using Acc - not used for anything right now)
translateAttribute(#localAttributeType{name=Name, type=Type, use=Use, ref=Ref}, _Acc) ->
  #attribute{name = Name, type=Type, optional=Use, ref=Ref};

translateAttribute(GroupRef = #attributeGroupRefType{}, _Acc) ->
  GroupRef.

%% -record(sequenceType, {annotation, elements}).
translateComplexTypeModel(undefined, Acc) ->
  {#typeInfo{elements = [], seqOrAll = sequence}, Acc};
translateComplexTypeModel(Sequence = #sequenceType{elements=Elements, minOccurs=Min, maxOccurs=Max},
                            Acc = #p1acc{seqCnt = Count, nsp = Prefix, path = Path, efd = Efd}) ->
  if 
    Min /= undefined; Max /= undefined ->
      %% create 1 element for the sequence (actuallly like a group), and a group-def-like 
      %% type
      %% debug("sequence with mx > 0"),
      Count2 = Count + 1,
      Name = "SEQ" ++ integer_to_list(Count2),
      TypeName = erlsom_lib:makeName(Name, Efd, Path, Prefix),
      Element = #elementInfo{alternatives=[#alternative{tag="##TODO", type= TypeName, real=false}], 
                     min=minMax(Min), max=minMax(Max)}, 
      {SeqType, Acc2} = transformGroupDef(#groupDefType{name= Path ++ Name, 
           model=Sequence#sequenceType{minOccurs = undefined, maxOccurs = undefined}}, Acc#p1acc{seqCnt = Count2}),
      ResultSoFar = Acc2#p1acc.tps,
      {#typeInfo{elements = [Element], seqOrAll = sequence, 
                 min = minMax(Min), max = minMax(Max)}, Acc2#p1acc{tps = [SeqType | ResultSoFar]}};
    true ->
      {Elements2, Acc2} = translateSequence(Elements, [], Acc),
      {#typeInfo{elements = Elements2, seqOrAll = sequence, 
                 min = minMax(Min), max = minMax(Max)}, Acc2}
  end;


%% special case for simpleContentType
translateComplexTypeModel(SCType = #simpleContentType{}, Acc = #p1acc{}) ->
  %% add a type for this element, with the attributes (if there are any).
  %% this will be a 'mixed' type: it will have a '#text' alternative
  Element = #elementInfo{alternatives=[#alternative{tag="#text", type="##string", real=false}], 
                                             min=0, max=1}, 
  {TheAttributes, AnyAttr} = translateSimpleContentTypeModel(SCType, Acc),
  {#typeInfo{elements = [Element], seqOrAll = text, attributes=TheAttributes, anyAttr = AnyAttr}, Acc};

%% -record(complexContentType, {annotation, model}).
translateComplexTypeModel(#complexContentType{model = Model}, Acc) ->
  translateComplexTypeModel(Model, Acc);

translateComplexTypeModel(#allType{elements=Elements, minOccurs=Min, maxOccurs=Max}, Acc) ->
  if 
    %% Min /= undefined, Max /= undefined ->
      %% throw({error, "MinOccurs and MaxOccurs attributes not supported for sequence. Use group?"});
    true ->
      {Elements2, Acc2} = translateSequence(Elements, [], Acc),
      {#typeInfo{elements = Elements2, seqOrAll = all, 
                 min = minMax(Min), max = minMax(Max)}, Acc2}
  end;

%% -record(anyType, {elInfo, any, minOccurs, maxOccurs, namespace, processContents}).
%% -record(anyInfo, {prCont, ns}). %% for any elements
%% -record(alt, {tag, tp, nxt = [], mn = 1, mx = 1, rl = true, anyInfo}).
translateComplexTypeModel(#anyType{minOccurs=Min, maxOccurs = Max, namespace = Ns, processContents = Pc}, Acc) ->
   AnyInfo = #anyInfo{prCont = case Pc of undefined -> "strict"; _ -> Pc end, 
                     ns = case Ns of undefined -> "##any"; _ -> Ns end}, 
   Elements = [#elementInfo{alternatives=[#alternative{tag="#any", type="any", real=[], anyInfo = AnyInfo}], 
                            min= minMax(Min), max= minMax(Max)}], 
   {#typeInfo{elements = Elements, seqOrAll = any}, Acc};
%% -record(groupRefType, {ref, minOccurs, maxOccurs}).
translateComplexTypeModel(#groupRefType{ref=Ref, minOccurs=Min, maxOccurs = Max}, Acc = #p1acc{nss = Nss}) ->
  Elements = [#elementInfo{alternatives=[#alternative{tag="##TODO", type=erlsom_lib:makeType(Ref, Nss), real=false}], 
                           min= minMax(Min), max= minMax(Max)}], 
  {#typeInfo{elements = Elements, seqOrAll = groupRef}, Acc};


%% -record(choiceType, {id, minOccurs, maxOccurs, alternatives}).
translateComplexTypeModel(#choiceType{minOccurs=Min, maxOccurs = Max, alternatives=Alternatives}, Acc) ->
  {TheAlternatives, Acc2} = translateAlternatives(Alternatives, [], Acc),
  Element = #elementInfo{alternatives=TheAlternatives, min=minMax(Min), max=minMax(Max)}, 
  {#typeInfo{elements = [Element], seqOrAll = choice}, Acc2};
  
%% -record(extensionTypeC, {base, annotation, model, attributes}).
translateComplexTypeModel(#extensionTypeC{base=Base, model = Model, attributes = Attributes, anyAttribute = AnyAttr}, Acc) ->
   {Type, Acc2} = translateComplexTypeModel(Model, Acc),
   TheAttributes = translateAttributes(Attributes, Acc),
   AnyAttrValue = translateAnyAttrValue(AnyAttr),
   {Type#typeInfo{extends = Base, attributes = TheAttributes, anyAttr = AnyAttrValue}, Acc2};

%% -record(restrictionTypeC, {base, annotation, model, attributes}).
translateComplexTypeModel(#restrictionTypeC{model = Model, attributes = Attributes, anyAttribute = AnyAttr}, Acc) ->
   {Type, Acc2} = translateComplexTypeModel(Model, Acc),
   TheAttributes = translateAttributes(Attributes, Acc),
   {Type#typeInfo{attributes = TheAttributes, anyAttr = AnyAttr}, Acc2}. 

translateAlternatives([Alternative | Tail], AlternativesSoFar, Acc) ->
  Path = Acc#p1acc.path,
  {TheAlternative, Acc2} = translateAlternative(Alternative, Acc),
  translateAlternatives(Tail, [TheAlternative | AlternativesSoFar], Acc2#p1acc{path = Path});

translateAlternatives([], AlternativesSoFar, Acc) ->
  {lists:reverse(AlternativesSoFar), Acc}.

%% -record(localElementType, {name, type, ref, minOccurs, maxOccurs, simpleOrComplex}).
translateSequence([LocalElement = #localElementType{} |Tail], ElementsSoFar, Acc) ->
  {Element, Acc2} = translateLocalElement(LocalElement, Acc),
  translateSequence(Tail, [Element | ElementsSoFar], Acc2);
translateSequence([ChoiceElement = #choiceType{} |Tail], ElementsSoFar, Acc) ->
  {Element, Acc2} = translateElement(ChoiceElement, Acc),
  translateSequence(Tail, [Element | ElementsSoFar], Acc2);
translateSequence([AnyElement = #anyType{} |Tail], ElementsSoFar, Acc) ->
  {Element, Acc2} = translateElement(AnyElement, Acc),
  translateSequence(Tail, [Element | ElementsSoFar], Acc2);
translateSequence([GroupElement = #groupRefType{} |Tail], ElementsSoFar, Acc) ->
  {Element, Acc2} = translateElement(GroupElement, Acc),
  translateSequence(Tail, [Element | ElementsSoFar], Acc2);
translateSequence([SequenceElement = #sequenceType{} | Tail], ElementsSoFar, Acc) ->
  {Element, Acc2} = translateSequenceInSequence(SequenceElement, Acc),
  translateSequence(Tail, [Element | ElementsSoFar], Acc2);

translateSequence([], ElementsSoFar, Acc) ->
  {lists:reverse(ElementsSoFar), Acc}.
  
translateElement(#choiceType{minOccurs=Min, maxOccurs = Max, alternatives=Alternatives}, Acc) ->
  {TheAlternatives, Acc2} = translateAlternatives(Alternatives, [], Acc),
  Element = #elementInfo{alternatives=TheAlternatives, min=minMax(Min), max=minMax(Max)}, 
  {Element, Acc2};

translateElement(#anyType{minOccurs=Min, maxOccurs = Max, namespace = Ns, processContents = Pc}, Acc) ->
  AnyInfo = #anyInfo{prCont = case Pc of undefined -> "strict"; _ -> Pc end, 
                     ns = case Ns of undefined -> "##any"; _ -> Ns end}, 
  Element = #elementInfo{alternatives=[#alternative{tag="#any", type="any", real=[], anyInfo = AnyInfo}], min=minMax(Min), 
                         max=minMax(Max)}, 
  {Element, Acc};

translateElement(#groupRefType{ref=Ref, minOccurs=Min, maxOccurs = Max}, Acc = #p1acc{nss = Nss}) ->
  Element = #elementInfo{alternatives=[#alternative{tag="##TODO", type=erlsom_lib:makeType(Ref, Nss), real=false}], 
                           min=minMax(Min), max=minMax(Max)}, 
  {Element, Acc}.

translateSequenceInSequence(Sequence = #sequenceType{minOccurs=Min, maxOccurs=Max}, 
                            Acc = #p1acc{seqCnt = Count, nsp = Prefix, path = Path, efd = Efd}) ->
  %% sequence in sequence is like a group-ref. The sequence itself is the group.
  Count2 = Count + 1,
  Name = "SEQ" ++ integer_to_list(Count2),
  TypeName = erlsom_lib:makeName(Name, Efd, Path, Prefix),
  Element = #elementInfo{alternatives=[#alternative{tag="##TODO", type= TypeName, real=false}], 
                 min=minMax(Min), max=minMax(Max)}, 
  {SeqType, Acc2} = transformGroupDef(#groupDefType{name= Path ++ Name, 
           model=Sequence#sequenceType{minOccurs = undefined, maxOccurs = undefined}}, Acc#p1acc{seqCnt = Count2}),
  ResultSoFar = Acc2#p1acc.tps,
  {Element, Acc2#p1acc{tps = [SeqType | ResultSoFar]}}.

%% -record(elementInfo, {alternatives, min, max}).
%% -record(alternative, {tag, type, real}).
translateLocalElement(Element = #localElementType{minOccurs=Min, maxOccurs = Max}, Acc) ->
  {Alternative, Acc2} = translateQuasiAlternative(Element, Acc),
  {#elementInfo{alternatives= [Alternative], min=minMax(Min), max=minMax(Max)}, Acc2}.

translateAlternative(#localElementType{type=undefined, ref=Ref, simpleOrComplex=undefined,
                                       minOccurs=Min, maxOccurs=Max}, Acc = #p1acc{nss = Nss}) when
  Ref /= undefined ->
  {#alternative{tag= erlsom_lib:makeRef(Ref, Nss), type=erlsom_lib:makeType(Ref, Nss), real = true,
                min=minMax(Min), max=minMax(Max)}, Acc};

%% A child element exists - russian doll design
translateAlternative(#localElementType{name=Name, type=undefined, ref=undefined, simpleOrComplex=SorC,
                     minOccurs=Min, maxOccurs=Max}, Acc = #p1acc{efd = Efd, nsp = Prefix, path = Path}) 
   when SorC /= undefined ->
   case SorC of 
      #localSimpleTypeType{} ->
         TypeRef = "##string",
         Acc3 = Acc;
      #localComplexTypeType{} ->
         {Type, Acc2} = translateLocalComplexType(SorC, Acc#p1acc{path= Path ++ Name ++ "/"}),
         ResultSoFar = Acc2#p1acc.tps,
         TypeRef = erlsom_lib:makeName(Name, Efd, Path, Prefix),
         ThisType = Type#typeInfo{typeName= TypeRef, global = false, 
                                  typeType = localElement},
         Types = [ThisType | ResultSoFar],
         Acc3 = Acc2#p1acc{tps = Types}
   end,
   {#alternative{tag=erlsom_lib:makeTag(Name, Prefix, Efd), type=TypeRef, real = true, min=minMax(Min), 
                 max=minMax(Max)}, Acc3};

translateAlternative(#localElementType{name=Name, type=Type, ref=undefined, simpleOrComplex=undefined,
                                       minOccurs=Min, maxOccurs = Max}, 
                     Acc = #p1acc{efd = Efd, nsp = Prefix, nss = Nss}) ->
   %% {#alternative{tag=erlsom_lib:makeTag(Name, Prefix, Efd), type=Type, real = true, min=minMax(Min), 
   {#alternative{tag=erlsom_lib:makeTag(Name, Prefix, Efd), type=erlsom_lib:makeType(Type, Nss), 
                 real = true, min=minMax(Min), max=minMax(Max)}, Acc};
%% -record(groupRefType, {ref, minOccurs, maxOccurs}).
translateAlternative(#groupRefType{ref=Ref, minOccurs=Min, maxOccurs=Max}, Acc = #p1acc{nss = Nss}) ->
  {#alternative{tag="##TODO", type=erlsom_lib:makeType(Ref, Nss), real=false, min=minMax(Min), max=minMax(Max)}, Acc};
%% HERE
translateAlternative(#anyType{minOccurs=Min, maxOccurs = Max, namespace = Ns, processContents = Pc}, Acc) ->
   AnyInfo = #anyInfo{prCont = case Pc of undefined -> "strict"; _ -> Pc end, 
                     ns = case Ns of undefined -> "##any"; _ -> Ns end}, 
  {#alternative{tag="#any", type="any", real=[], anyInfo = AnyInfo}, Acc};
translateAlternative(Sequence = #sequenceType{minOccurs=Min, maxOccurs=Max}, 
                     Acc = #p1acc{efd = Efd, nsp = Prefix, path = Path, seqCnt = Count}) ->
  %% debug("sequence in sequence"),
  Count2 = Count + 1,
  Name = "SEQ" ++ integer_to_list(Count2),
  TypeName = erlsom_lib:makeName(Name, Efd, Path, Prefix),
  %% !! TODO: should minOccurs and maxOccurs be copied to the 
  %% alternative or to the groupDef??
  Alternative = #alternative{tag="##TODO", type=TypeName, real=false, 
                             min=minMax(Min), max=minMax(Max)}, 
  {SeqType, Acc2} = transformGroupDef(#groupDefType{name= Path ++ Name, 
           model=Sequence#sequenceType{minOccurs = undefined, maxOccurs = undefined}}, Acc#p1acc{seqCnt = Count2}),
  ResultSoFar = Acc2#p1acc.tps,
  {Alternative, Acc2#p1acc{tps = [SeqType | ResultSoFar]}};

translateAlternative(Choice = #choiceType{minOccurs=Min, maxOccurs=Max}, 
                     Acc = #p1acc{efd = Efd, nsp = Prefix, path = Path, seqCnt = Count}) ->
  Count2 = Count + 1,
  Name = "CH" ++ integer_to_list(Count2),
  TypeName = erlsom_lib:makeName(Name, Efd, Path, Prefix),
  Alternative = #alternative{tag="##TODO", type=TypeName, real=false, 
                             min=1, max=1}, 
  {ChoiceType, Acc2} = transformGroupDef(#groupDefType{name= Path ++ Name, 
           model=Choice#choiceType{minOccurs = Min, maxOccurs = Max}}, Acc#p1acc{seqCnt = Count2}),
  ResultSoFar = Acc2#p1acc.tps,
  {Alternative, Acc2#p1acc{tps = [ChoiceType | ResultSoFar]}}.

%% quasi alternative - outside a choice
translateQuasiAlternative(#localElementType{type=undefined, ref=Ref, simpleOrComplex=undefined}, 
                                            Acc = #p1acc{nss = Nss}) when Ref /= undefined ->
  {#alternative{tag= erlsom_lib:makeRef(Ref, Nss), type=erlsom_lib:makeType(Ref, Nss), real = true}, Acc};

%% A child element exists - russian doll design
translateQuasiAlternative(#localElementType{name=Name, type=undefined, ref=undefined, simpleOrComplex=SorC},
                          Acc = #p1acc{efd = Efd, nsp = Prefix, path = Path})
   when SorC /= undefined ->
   case SorC of 
      #localSimpleTypeType{} ->
         TypeRef = "##string",
         Acc3 = Acc;
      #localComplexTypeType{} ->
         {Type, Acc2} = translateLocalComplexType(SorC, Acc#p1acc{path= Path ++ Name ++ "/"}),
         ResultSoFar = Acc2#p1acc.tps,
         TypeRef = erlsom_lib:makeName(Name, Efd, Path, Prefix),
         ThisType = Type#typeInfo{typeName= TypeRef, global = false, 
                                  typeType = localElement},
         Types = [ThisType | ResultSoFar],
         Acc3 = Acc2#p1acc{tps = Types}
   end,
   {#alternative{tag=erlsom_lib:makeTag(Name, Prefix, Efd), type=TypeRef, real = true}, Acc3#p1acc{path=Path}};

translateQuasiAlternative(#localElementType{name=Name, type=Type, ref=undefined, simpleOrComplex=undefined}, 
                          Acc = #p1acc{efd = Efd, nsp = Prefix, nss = Nss}) ->
   {#alternative{tag=erlsom_lib:makeTag(Name, Prefix, Efd), type=erlsom_lib:makeType(Type, Nss), real = true}, Acc};
%% -record(groupRefType, {ref, minOccurs, maxOccurs}).
translateQuasiAlternative(#groupRefType{ref=Ref}, Acc = #p1acc{nss = Nss}) ->
  {#alternative{tag="##TODO", type=erlsom_lib:makeType(Ref, Nss), real=false}, Acc}.
